#include "FreeRTOS.h"
#include "task.h"
#include "diag.h"
#include "main.h"
#include <example_entry.h>

extern void console_init(void);

#include <stdio.h>
#include <errno.h>
#include <lwip/sockets.h>
#include <lwip_netconf.h>
#include "timer_api.h"
#include "wifi_conf.h"

#define EXAMPLE_WIFI_SSID "smroute"
#define EXAMPLE_WIFI_PASS "LXY960712honor"

#define SERVER_STR_ADDR "192.168.0.114"
#define SERVER_PORT 8366

#define SEND_BLOCK_SIZE 1024
#define SEND_BLOCK_NUM 10

#define CLIENT_HDLE_INVL_USEC 40000

#define SOCKET_WIN_SIZE 4096

#define TIMER_PERIOD 0xFFFFFFFF

#define PRINTF(fmt, ...) do{\
		uint64_t now = 0xFFFFFFFF - gtimer_read_us(&mono_time);\
        printf("I {line %d}[%lld]: " fmt "\n", __LINE__, now / 1000, ##__VA_ARGS__);\
    } while(0)
#define PERROR() printf("E {line %d}[%d]: %s\n", __LINE__, errno, strerror(errno))

static uint8_t s_recv_buff[SEND_BLOCK_SIZE] = {0};

gtimer_t mono_time;

extern void fATW0(void *arg);
extern void fATW1(void *arg);
extern void fATWC(void *arg);
extern void fATWD(void *arg);

static void initialise_wifi(void)
{
	do
	{
		PRINTF("try once");
		fATW0(EXAMPLE_WIFI_SSID);
		fATW1(EXAMPLE_WIFI_PASS);
		fATWC(NULL);
		// vTaskDelay(3000 / portTICK_PERIOD_MS);
	} while (wifi_is_connected_to_ap());
	
}

static void deinitialise_wifi(void)
{
	fATWD(NULL);
}

static void example_tcp_flow_ctrl_test_client_thread(void *param)
{
	/* To avoid gcc warnings */
	( void ) param;
	gtimer_init(&mono_time, TIMER2);
	gtimer_start_periodical(&mono_time, TIMER_PERIOD, NULL, NULL);
	vTaskDelay(3000 / portTICK_PERIOD_MS);
    initialise_wifi();

    int recv_bytes_cnt = 0;
    int send_bytes_cnt = 0;

    int client_fd = socket(AF_INET, SOCK_STREAM, 0);
    int win_size = SOCKET_WIN_SIZE;
    int ret = setsockopt(client_fd, SOL_SOCKET, SO_RCVBUF, &win_size, sizeof(win_size));
    if(0 > ret)
    {
        PERROR();
        goto fail;
    }
    // ret = setsockopt(client_fd, SOL_SOCKET, SO_SNDBUF, &win_size, sizeof(win_size));
    // if(0 > ret)
    // {
    //     PERROR();
    //     goto fail;
    // }

    struct sockaddr_in server_addr = 
    {
        .sin_family = AF_INET,
        .sin_addr.s_addr = inet_addr(SERVER_STR_ADDR),
        .sin_port = htons(SERVER_PORT),
    };
    ret = connect(client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if(0 > ret)
    {
        PERROR();
        goto fail;
    }
	PRINTF("tcp flow control test client start");
    int bytes = 0;
    uint64_t start, end, intvl;
    start = gtimer_read_us(&mono_time);
    while(1)
    {
        int bytes_in_block = 0;
        while(bytes_in_block < SEND_BLOCK_SIZE)
        {
            int bytes = recv(client_fd, s_recv_buff + bytes_in_block, SEND_BLOCK_SIZE - bytes_in_block, 0);
            if(bytes > 0)
            {
                // PRINTF("recv %d bytes once", bytes);
                bytes_in_block += bytes;
                recv_bytes_cnt += bytes;
            }
            else if(0 == bytes)
            {
                PRINTF("server close socket");
                goto success;
            }
            else
            {
                PERROR();
                goto fail;
            }
        }
        // PRINTF("recv one block");
		vTaskDelay(CLIENT_HDLE_INVL_USEC / 1000 / portTICK_PERIOD_MS);
        bytes_in_block = 0;
        while(bytes_in_block < SEND_BLOCK_SIZE)
        {
            int bytes = send(client_fd, s_recv_buff + bytes_in_block, SEND_BLOCK_SIZE - bytes_in_block, 0);
            if(bytes > 0)
            {
                bytes_in_block += bytes;
                send_bytes_cnt += bytes;
            }
            else if(0 == bytes)
            {
                PRINTF("send 0 bytes");
            }
            else
            {
                PERROR();
                goto fail;
            }
        }
        // PRINTF("send one block");
    }

success:
    end = gtimer_read_us(&mono_time);
	intvl = start - end;
    PRINTF("sum of recv %d bytes", recv_bytes_cnt);
    PRINTF("sum of send %d bytes", send_bytes_cnt);
    PRINTF("tcp flow control test client end spend %ld.%ld Sec", intvl / 1000000, intvl / 1000);
    close(client_fd);

    vTaskDelay(1000 / portTICK_PERIOD_MS);
    deinitialise_wifi();
    PRINTF("FIN");
	vTaskDelete(NULL);
    return;
fail:
    if(client_fd > 0)
    {
        close(client_fd);
    }

    vTaskDelay(1000 / portTICK_PERIOD_MS);
    deinitialise_wifi();
    PRINTF("FIN");
	vTaskDelete(NULL);
    return;
}

void example_tcp_flow_ctrl_test_client(void)
{
	if(xTaskCreate(example_tcp_flow_ctrl_test_client_thread, ((const char*)"example_tcp_flow_ctrl_test_client_thread"), 1024, NULL, tskIDLE_PRIORITY + 1, NULL) != pdPASS)
		printf("\n\r%s xTaskCreate(init_thread) failed", __FUNCTION__);
};

/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */
int main(void)
{
	/* Initialize log uart and at command service */
	console_init();

	/* pre-processor of application example */
	pre_example_entry();

	/* wlan intialization */
	wlan_network();

	/* Execute application example */
	example_entry();

	example_tcp_flow_ctrl_test_client();

	/* Enable Schedule, Start Kernel */
	vTaskStartScheduler();

	/* Should NEVER reach here */
	return 0;
}